/**
 * FireFly (2011)
 */
package net.abbp.serial;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.comm.CommDriver;
import javax.comm.CommPortIdentifier;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;

import net.abbp.mtr.event.SerialEvent;
import net.abbp.mtr.event.SerialListener;

/**
 * 简单串口操作
 * 
 * @version 1.0 2011-1-9
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since FireFly 1.0
 */
public class SimpleSerial implements SerialPortEventListener {
    private InputStream inputStream;
    private OutputStream outputStream;
    private CommPortIdentifier portId;
    private SerialPort serialPort;
    private List<SerialListener> serialLstnrs;
    // 端口是否打开
    private boolean open;

    /**
     * 构造函数
     */
    public SimpleSerial() {
        serialLstnrs = new ArrayList<SerialListener>();
    }

    /**
     * 添加串口事件监听器
     * 
     * @param lstnr
     *            监听器
     */
    public synchronized void addSerialEventListener(SerialListener lstnr) {
        this.serialLstnrs.add(lstnr);
    }

    /**
     * 移除串口监听器
     * 
     * @param lstnr
     *            监听器
     */
    public synchronized void removeSerialEventListener(SerialListener lstnr) {
        this.serialLstnrs.remove(lstnr);
    }

    /**
     * 触发串口事件
     */
    private void fireSerialEvent(String data) {
        SerialEvent evt = new SerialEvent(this, data);

        for (SerialListener lstnr : this.serialLstnrs) {
            lstnr.onSerialEvent(evt);
        }
    }

    /**
     * 初始化
     */
    public void init() {
        try {
            System.out.println("初始化串口");
            Properties prop = new Properties();
            prop.load(new FileInputStream("mtr.properties"));

            System.loadLibrary(prop.getProperty("library", "win32com"));
            String driverName = prop.getProperty("driver", "com.sun.comm.Win32Driver");
            CommDriver driver;
            driver = (CommDriver) Class.forName(driverName).newInstance();
            driver.initialize();
            System.out.println(prop.getProperty("com_data"));
            portId = CommPortIdentifier.getPortIdentifier(prop.getProperty("com_data"));

            serialPort = (SerialPort) portId.open("MTRAPP", 20000);

            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);
            serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

            this.open = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭串口
     */
    public void close() {
        serialPort.close();
    }

    /**
     * 串口写入字节数据
     * 
     * @param data
     *            字节数据
     */
    public void writePort(byte[] data) {
        try {
            outputStream.write(data);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                outputStream.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * 端口写入十六进制字符串
     * 
     * @param hexStr
     *            十六进制字符串
     */
    public void writePortHexString(String hexStr) {
        writePort(hexStringToBytes(hexStr));
    }

    /**
     * 十六进制字符串转换为字节数据
     * 
     * @param hexString
     *            十六进制数字字符串
     * @return 字节数据
     */
    public static byte[] hexStringToBytes(String hexString) {
        if ((hexString == null) || (hexString.equals(""))) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; ++i) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[(pos + 1)]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
        case SerialPortEvent.BI:/* Break interrupt,溢位错误 */
            System.out.println("溢位错误");
            break;
        case SerialPortEvent.OE:/* Overrun error，溢位错误 */
            System.out.println("溢位错误");
            break;
        case SerialPortEvent.FE:/* Framing error，传帧错误 */
            System.out.println("传帧错误");
            break;
        case SerialPortEvent.PE:/* Parity error，校验错误 */
            System.out.println("校验错误");
            break;
        case SerialPortEvent.CD:/* Carrier detect，载波检测 */
            System.out.println("载波检测");
            break;
        case SerialPortEvent.CTS:/* Clear to send，清除发送 */
            System.out.println("清除发送");
            break;
        case SerialPortEvent.DSR:/* Data set ready，数据设备就绪 */
            System.out.println("数据设备就绪");
            break;
        case SerialPortEvent.RI:/* Ring indicator，响铃指示 */
            System.out.println("响铃指示");
            break;
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*
                                                  * Output buffer is
                                                  * empty，输出缓冲区清空
                                                  */
            System.out.println("输出缓冲区清空");
            break;
        case SerialPortEvent.DATA_AVAILABLE:/*
                                             * Data available at the serial
                                             * port，端口有可用数据。读到缓冲数组，输出到终端
                                             */
            byte[] readBuffer = new byte[20];
            String readStr = "";
            int numBytes = 0;
            try {
                while (inputStream.available() > 0) {
                    numBytes = inputStream.read(readBuffer);
                }
                for (int i = 0; i < numBytes; i++) {
                    readStr = readStr + (char) readBuffer[i];
                }

                this.fireSerialEvent(readStr);
            } catch (IOException e) {
                e.printStackTrace();
            }
            break;
        }
    }

    public void setOpen(boolean open) {
        this.open = open;
    }

    public boolean isOpen() {
        return open;
    }
}
